import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.io.File;

/**
 * @modulename POTtable
 * @description This module takes in a file specified by the user and stores it
 *              line by line in a record of arraylists, which is the
 *              representation of the POTtable class. 
 * @callsequence POTtable testPOT = new POTtable(File potText); where testPOT is
 *               the given variable name by the user and potText is a File
 *               containing the POT directives.
 * @inparam A File containing the POT directives
 * @outparam Creates a POTtable table that contains the directives and
 *           information about the directives.
 * @errorstested tested by feeding the POTtable the directives contained in
 *               POTtable.txt
 * @errorsgenerated Initially, we had errors with separating the different
 *                  fields of the POTtable because we were using a tab character
 *                  as the deliminator and the last field of the pot table didnt
 *                  have a tab character after this. This was fixed by adding
 *                  tabs at the end of each directive line in POTtable.txt.
 * @author Nicholas Lister
 * @createdate 10/11/09
 * @modlog <p>
 *         <ul>
 *         Who/When/Why
 *         </ul>
 *         Coding Standards Met: "Signed Code Leader(Jaron)"
 *         <p>
 *         Testing Standards Met: "Signed Test Leader(Jamie)"
 */
public class POTtable {

	private static List<String> labelArray;
	private static List<String> directiveArray;
	private static List<String> operandArray;
	private static List<String> descriptionArray;
	private static List<String> consumesMemoryArray;
	
	public static void startPOTtable() {
		labelArray = new ArrayList<String>(0);
		directiveArray = new ArrayList<String>(0);
		operandArray = new ArrayList<String>(0);
		descriptionArray = new ArrayList<String>(0);
		consumesMemoryArray = new ArrayList<String>(0);
		setUp();
	}
	
	private static void setUp() {
		File potTable = new File("src" + Main.systemPathSeparator + "POTtable.txt");
		String potTableFileString = FileHandler.readFile(potTable);
		Scanner potScanner = new Scanner(potTableFileString);
		String potTableString = new String();
		String tempString = new String();
		// by reading the whole text file into a string, and using newlines as separators
		//we effectively get rid of the newlines
		while (potScanner.hasNextLine()) {
			tempString = potScanner.nextLine();
			potTableString = potTableString + tempString;
		}
		potScanner.close();
		Scanner scanOb1 = new Scanner(potTableString).useDelimiter("\\s*\t\\s*");
		//without newlines, we have a string that used to be 6 columns, each column separated by a tab.
		//this means that if we tab deliminate we can separate the columns into their respective arrays.
		while(scanOb1.hasNext())
		{
			tempString = scanOb1.next();
			labelArray.add(tempString);
			tempString = scanOb1.next();
			directiveArray.add(tempString.toLowerCase());
			tempString = scanOb1.next();
			operandArray.add(tempString);
			tempString = scanOb1.next();
			descriptionArray.add(tempString);
			tempString = scanOb1.next();
			consumesMemoryArray.add(tempString);
		}
	}

	/**
	 * @modulename isDefined
	 * @description This module returns a Boolean value representing whether or
	 *              not a given directive is defined in the POTtable.
	 * @callsequence testPOT.isDefined(String check); where testPOT is the given
	 *               variable name by the user.
	 * @inparam A String representing the directive to be checked in the
	 *          POTtable
	 * @outparam A Boolean value representing whether or not a given directive
	 *           is defined in the POTtable.
	 * @errorstested tested by feeding the POTtable the directives contained in
	 *               POTtable.txt
	 * @errorsgenerated no known errors
	 * @author Nicholas Lister
	 * @createdate 10/11/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */

	public static Boolean isDefined(String directive) {
		if (directive != null) {
			String lower = directive.toLowerCase();
			return (directiveArray.contains(lower));
		} else {
			return Boolean.FALSE;
		}
	}

	/**
	 * @modulename canHaveLabel
	 * @description This module returns a Boolean value representing whether or
	 *              not a given directive can have a label.
	 * @callsequence testPOT.canHaveLabel(String directive); where testPOT is
	 *               the given variable name by the user.
	 * @inparam A String representing a directive
	 * @outparam A Boolean value representing whether or not a given directive
	 *           can have a label.
	 * @errorstested tested by feeding the POTtable the directives contained in
	 *               POTtable.txt
	 * @errorsgenerated no known errors
	 * @author Nicholas Lister
	 * @createdate 10/11/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static Boolean canHaveLabel(String directive) {
		String lower = directive.toLowerCase();
		String label = labelArray.get(directiveArray.indexOf(lower));
		if (label.equals("none")) {
			return false;
		}
		return true;
	}

	/**
	 * @modulename requiresLabel
	 * @description This module returns a Boolean value representing whether or
	 *              not a given directive requires a label.
	 * @callsequence testPOT.requiresLabel(String directive); where testPOT is
	 *               the given variable name by the user.
	 * @inparam A String representing a directive
	 * @outparam A Boolean value representing whether or not a given directive
	 *           requires a label.
	 * @errorstested tested by feeding the POTtable the directives contained in
	 *               POTtable.txt
	 * @errorsgenerated no known errors
	 * @author Nicholas Lister
	 * @createdate 10/11/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static Boolean requiresLabel(String directive) {
		String lower = directive.toLowerCase();
		String label = labelArray.get(directiveArray.indexOf(lower));
		if (label.equals("rl")) {
			return true;
		}
		return false;
	}

	/**
	 * @modulename operandType
	 * @description This module returns the operandType of a given directive.
	 * @callsequence testPOT.operandType(String directive); where testPOT is the
	 *               given variable name by the user.
	 * @inparam A String representing a directive
	 * @outparam An operandType enum representing the operand type of a given
	 *           directive.
	 * @errorstested tested by feeding the POTtable the directives contained in
	 *               POTtable.txt
	 * @errorsgenerated no known errors
	 * @author Nicholas Lister
	 * @createdate 10/11/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why Travis 10/17/09 fixed error with ex-label
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static OperandType.operandType operandType(String directive) {
		String lower = directive.toLowerCase();
		String tempString = operandArray.get(directiveArray.indexOf(lower));
		System.out.println("POTtable\tDIRECTIVE given to POT Table: "
				+ directive);
		System.out.println("POTtable\tIndex of given DIRECTIVE: "
				+ directiveArray.indexOf(lower));
		System.out.println("POTtable\tOperand Type Checked: " + tempString);
		boolean isAnExternal = (tempString.contains("Address constant"));
		System.out.println(isAnExternal);
		if (tempString.equals("type 1")) {
			return OperandType.operandType.TYPE1;
		} else if (tempString.equals("type 2")) {
			return OperandType.operandType.TYPE2;
		} else if (tempString.equals("type 3")) {
			return OperandType.operandType.TYPE3;
		} else if (tempString.equals("type 4")) {
			return OperandType.operandType.TYPE4;
		} else if (tempString.equals("EXPRESSION 1")) {
			System.out.println("POTtable\tReturning OPERAND TYPE: EXPRESSION 1");
			return OperandType.operandType.EXPRESSION1;
		} else if (tempString.equals("EXPRESSION 2")) {
			return OperandType.operandType.EXPRESSION2;
		} else if (tempString.equals("EXPRESSION 3")) {
			return OperandType.operandType.EXPRESSION3;
		} else if (tempString.equals("REF-ALL")) {
			return OperandType.operandType.REFALL;
		} else if (tempString.equalsIgnoreCase("Reference")) {
			return OperandType.operandType.REF1;
		} else if (tempString.equals("#Reference")) {
			return OperandType.operandType.REF2;
		} else if (tempString.equals("%Reference")) {
			return OperandType.operandType.REF3;
		} else if (tempString.equals("Reference-Exp")) {
			return OperandType.operandType.REFEXP;
		} else if (tempString.contains("On or Off")) {
			return OperandType.operandType.ONOFF;
		} else if (tempString.contains(",label")) {
			return OperandType.operandType.LABEL;
		} else if (tempString.contains("ex-label")) {
			// CHANGED from
			// (tempString.equals("ex-label,ex-label,ex-label,ex-label")
			// As it would not return true.
			System.out.println("POTtable\tReturning OPERAND TYPE: EXLABEL");
			return OperandType.operandType.EXLABEL;
		} else if (tempString.equals("base 10 number right justified")) {
			return OperandType.operandType.NUM;
		} else if (tempString.equals("'ccc' left justified")) {
			return OperandType.operandType.CHR;
		} else if (tempString.equals("hex string right justified")) {
			return OperandType.operandType.HEX;
		} else if (tempString.equals("binary string right justified")) {
			return OperandType.operandType.BIN;
		} else if (tempString.contains("Address constant")) {
			return OperandType.operandType.ADDR;
		} else if (tempString.equals("*")) {
			return OperandType.operandType.STAR;
		} else {
			System.out.println("POTtable\tReturning OPERAND TYPE: NONE");
			return OperandType.operandType.NONE;
		}
	}

	/**
	 * @modulename description
	 * @description This module returns the description of a given directive.
	 * @callsequence testPOT.description(String directive); where testPOT is the
	 *               given variable name by the user.
	 * @inparam A String representing a directive
	 * @outparam A String describing the use of a directive.
	 * @errorstested tested by feeding the POTtable the directives contained in
	 *               POTtable.txt
	 * @errorsgenerated no known errors
	 * @author Nicholas Lister
	 * @createdate 10/11/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String description(String directive) {
		String lower = directive.toLowerCase();
		return descriptionArray.get(directiveArray.indexOf(lower));
	}

	/**
	 * @modulename consumesMemory
	 * @description This module returns a Boolean value representing whether or
	 *              not a given directive consumes memory.
	 * @callsequence testPOT.consumesMemory(String directive); where testPOT is
	 *               the given variable name by the user.
	 * @inparam A String representing a directive
	 * @outparam A Boolean value representing whether or not a given directive
	 *           consumes memory.
	 * @errorstested tested by feeding the POTtable the directives contained in
	 *               POTtable.txt
	 * @errorsgenerated no known errors
	 * @author Nicholas Lister
	 * @createdate 10/11/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static Boolean consumesMemory(String directive) {
		if (isDefined(directive)) {
			String lower = directive.toLowerCase();
			String consumesMemory = consumesMemoryArray.get(directiveArray
					.indexOf(lower));
			if (consumesMemory.equals("YES")) {
				return true;
			}
			return false;
		} else {
			return false;
		}
	}

	// test module
	public static void main(String[] args) {
		POTtable.startPOTtable();

		testDirective("Start");
		testDirective("End");
		testDirective("RESET-LC");
		testDirective("Using");
		testDirective("DEBUG");
		testDirective("EQU");
		testDirective("EQU-exp");
		testDirective("Alt-start");
		testDirective("SKIP-mem");
		testDirective("Entry");
		testDirective("External");
		testDirective("word-num");
		testDirective("word-chr");
		testDirective("word-hex");
		testDirective("word-bin");
		testDirective("COMP");
		testDirective("Adr-comp");
		testDirective("Adr");
		testDirective("NOP");
		testDirective("Undefined Directive Test Case");
	}

	// test module
	public static void testDirective(String directive) {
		System.out.println(directive + " is defined? "
				+ POTtable.isDefined(directive));
		if (POTtable.isDefined(directive)) {
			System.out.println("Can have label? "
					+ POTtable.canHaveLabel(directive));
			System.out.println("Requires label? "
					+ POTtable.requiresLabel(directive));
			OperandType.operandType opType = POTtable.operandType(directive);
			System.out.println("Operand Type: " + opType);
			System.out.println("Consumes Memory? "
					+ POTtable.consumesMemory(directive));
			System.out.println();
		} else {
			System.out.println("ERROR: Undefined Directive");
		}
	}
}
